/**
 * 比较器模块
 * License: BSD
 * Authors: Lucifer (786325481@QQ.com)
 * Copyright: Copyright (C) 2008 Lucifer. All rights reserved.
 */
module system.collections.Comparer;

import system.math.IEEE;
import system.Traits;
import system.collections.IComparer;

/**
 * 默认排序顺序比较器。
 * Comments: 对于区分文化的字符串比较，建议使用 StringComparer 类，而不要使用 DefaultComparer!(String)。
 */
public class DefaultComparer(T) : IComparer!(T)
{
	static if( IsPointerType!(T) || (IsArray!(T) && !IsSomeString!(T)) )
	{
		static assert( "Type " ~ T.stringof ~ " is not supported." );
	}
    else static if( is( T == struct ) && !( is( typeof(T.opCmp) ) ) )
    {
        static assert(false, "Struct " ~ T.stringof ~ " should overload opCmp.");
    }

    private static DefaultComparer!(T) instance;

    static this()
    {
        instance = new DefaultComparer!(T)();
    }

	static ~this()
	{
		if(instance !is null)
		{
			delete instance;
			scope(failure) return;
		}
	}

    /**
     * 获取默认比较器实例。
     */
    public static DefaultComparer!(T) getInstance()
    {
        return instance;
    }

    /**
     * 对同一类型的两个对象执行比较并返回一个值，指示一个对象是小于、等于还是大于另一个对象。
     * Params: x = 要比较的第一个对象。
     *         y = 要比较的第二个对象。
     * Returns: 当 x > y 时，返回值大于零。
     *          当 x < y 时，返回值小于零。
     *          当 x == y 时，返回值等于零。
     */
    public final int compare(T x, T y)
    {
        //类类型
        static if( is( T == class ) )
        {
            // Regard null references as always being "less than"
            if( x !is null )
            {
                if( y is null )
					return 1;
				else
					return x.opCmp(y);
			}
			else
			{
				if( y is null )
					return 0;
				else
					return -1;
			}
        }
        //接口类型
        else static if( is( T == interface ) )
        {
            Interface* pi = **cast(Interface***)cast(void*)x;
            Object o1 = cast(Object)( cast(void*)x - pi.offset);
            pi = **cast(Interface***)cast(void*)y;
            Object o2 = cast(Object)( cast(void*)y - pi.offset);

            // Regard null references as always being "less than"
            if( o1 !is null )
            {
                if( o2 is null )
                    return 1;
                else
                    return o1.opCmp(o2);
            }
            else
            {
                if( o2 is null )
                    return 0;
                else
                    return -1;
            }
        }
        //整型和字符类型
        else static if( IsIntegerType!(T) || IsCharType!(T) )
        {
           return (x - y);
        }
        //实数和虚数类型
        else static if( IsRealType!(T) || IsImaginaryType!(T) )
        {
            if (x !<>= y)		// if either are NaN
            {
                if (isNaN(x))
                {
                    if (isNaN(y))
                        return 0;
                    return -1;
                }
                return 1;
            }
            return (x == y) ? 0 : ((x < y) ? -1 : 1);
        }
        //复数类型
        else static if( IsComplexType!(T) )
        {
            int result;

            if (x.re < y.re)
                result = -1;
            else if (x.re > y.re)
                result = 1;
            else if (x.im < y.im)
                result = -1;
            else if (x.im > y.im)
                result = 1;
            else
                result = 0;
            return result;
        }
        //结构类型
        else static if( is ( T == struct) && is(typeof(T.opCmp) == function)  )
        {
            return x.opCmp(y);
        }
        else
        {
            return typeid(T).compare(&x, &y);
        }
    }
}

unittest
{
    interface A
    {
    }

    class B : A
    {
        this(int i)
        {
            this.I = i;
        }
        int I;

        override int opCmp(Object other)
        {
            if(other is null) return 1;
            B b = cast(B)other;
            if(b !is null)
            {
                return this.I - b.I;
            }
            else
            {
                return 1;
            }
        }

        override bool opEquals(Object other)
        {
            if(other is null) return false;
            B b = cast(B)other;
            if(b is null) return false;
            return this.I == b.I;
        }
    }

    A a1 = new B(2);
    A a2 = new B(1);

    DefaultComparer!(A) cmpA = new DefaultComparer!(A)();
    assert( cmpA.compare(a1, a2) > 0 );
    //assert( cmpA.compare(a1, null) > 0 );
    //assert( cmpA.compare(null, null) == 0 );

    B b1 = cast(B)a1;
    B b2 = cast(B)a2;
    DefaultComparer!(B) cmpB = new DefaultComparer!(B)();
    assert( cmpB.compare(b1, b2) > 0 );
    assert( cmpB.compare(b1, null) > 0 );

    struct C
    {
        int I;
        int opCmp(C other)
        {
            return this.I - other.I;
        }
    }

    C c1, c2;
    c1.I = 2;
    c2.I = 1;
    DefaultComparer!(C) cmpC = new DefaultComparer!(C)();
    assert( cmpC.compare(c1, c2) > 0 );

    DefaultComparer!(int) cmpInt = new DefaultComparer!(int);
    assert( cmpInt.compare(2, 1) > 0 );

    DefaultComparer!(double) cmpDouble = new DefaultComparer!(double);
    assert( cmpDouble.compare(2.0L, 1.0L) > 0 );
    assert( cmpDouble.compare(2.0L, double.nan) > 0 );

    assert( DefaultComparer!(int).getInstance() !is null );
}
